package com.open.capacity.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.open.capacity.config.SendMessage;
import com.open.capacity.dao.OrderDao;
import com.open.capacity.model.MessageModel;
import com.open.capacity.model.OrderAndStation;
import com.open.capacity.redis.util.RedisUtil;
import com.open.capacity.service.OrderService;
import com.open.capacity.util.ListSortUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: y_022
 * @date: 9:29-2021/6/25
 * @description:
 */
@Service
@Slf4j
@Transactional
public class OrderServiceImpl implements OrderService {
    private static final String KEY_PRE_UNPAID = "ORDER_UNPAID"; //缓存中未支付的订单前缀
    private static final String KEY_PRE_USING = "ORDER_USING"; //缓存中正在使用中的订单前缀
    private static final String KEY_PRE_INVALID = "ORDER_INVALID"; //数据库中的无效订单前缀


    private static final String KEY_PRE_MESSAGE_COUNT = "KEY_PRE_MESSAGE_COUNT"; //缓存中的消息的总数前缀
    private static final String ADDORDER_REMIND = "你有一笔新产生订单未支付，下单工位："; //缓存中的消息内容
    private static final String PAY_SUCCESS_REMIND = "您的一笔新订单："; //支付成功后的消息内容

    private static final String DIRCR_EXCHAGNE_NAME = "MESSAGE_EXCHANGE"; //交换机
    private static final String QUEUE_NAME = "QUEUE"; //队列前缀
    private static final String ROUTING_KRY = "RoutingKey"; //路由键前缀




    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private SendMessage sendMessage;
    /**
     * 下订单操作，先放在缓存中，由于设置了订单过期时间，未支付订单只会保留指定时间，如果此时段内没有对缓存中订单进行支付操作，则此订单会从缓存中删除。
     * 同时在数据库中再存一份！
     *   ！！！！警告:上面的做法取消：改用了mq的延迟队列来实现订单超时自动取消。
     * @param orderModel
     * @return
     */
    @Override
    @Transactional
    public OrderAndStation addOrder(OrderAndStation orderModel) {
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(String.valueOf(orderModel.getStatid()), UUID.randomUUID(), 3, TimeUnit.SECONDS); //使用redis的分布式锁，来实现。
        if(aBoolean) {
            OrderAndStation orderModel1 = new OrderAndStation();
            try {
                //todo 在生产订单之前，应该判断该商品在下单的日期之内有没有被占用，如果被占用应该return一个结果集，结果集对应的实体类加一个字段，表示该日期有没有占用。
                Date date = new Date();
                //计算总价:单价*时长
                orderModel.setLetime(12 * orderModel.getTime().length);
                orderModel = orderDao.findStationByStatid(orderModel.getStatid());
                orderModel.setBusiid(orderModel1.getBusiid());
                orderModel.setUnitpr(orderModel1.getPrices());
                if (orderModel1.getActiid() != null) {
                    orderModel.setReduce(orderModel1.getReduce());
                } else {
                    orderModel.setReduce(1F);
                }
                orderModel.setStname(orderModel1.getStname());
                orderModel.setPrices(orderModel.getReduce() >= 1 ? orderModel.getUnitpr() * orderModel.getLetime() : orderModel.getUnitpr() * orderModel.getLetime() * orderModel.getReduce());
                //生成订单号:前缀：”sta“ +当前时间戳
                orderModel.setOrcode("ORDER" + System.currentTimeMillis());
                orderModel.setEnable(0);//将订单状态设为0-->未支付
                orderModel.setCrdate(date);
                orderModel.setUpdate(date);

                //存入使用时间记录
                String[] res = orderModel.getTime();
                for (int i = 0; i < res.length; i++) {
                    orderDao.addUsageTimeRecord(res[i], orderModel.getOrcode(), orderModel.getStatid());
                }

                //redis 存入未支付订单
                redisTemplate.opsForValue().set(KEY_PRE_UNPAID + orderModel.getUserid() + ":" + orderModel.getOrcode(), orderModel);
                redisTemplate.opsForValue().set(KEY_PRE_UNPAID + orderModel.getUserid() + ":" + orderModel.getOrcode(), orderModel, 1800, TimeUnit.SECONDS);

                orderDao.addOrder(orderModel); //存入数据库！
                //下单完成后需要向客户发送下单消息，消息总数存在缓存中，通过前端轮询来取，消息本身还是存在数据库中
                MessageModel messageModel = new MessageModel();
                messageModel.setConten(ADDORDER_REMIND + orderModel1.getStname());
                messageModel.setMetype(1);
                messageModel.setUserid(orderModel.getUserid());
                Integer count = 0;
                String key = KEY_PRE_MESSAGE_COUNT + orderModel.getUserid();
            } catch (Exception e) {
                log.error("出现错误，下单失败！", e);
            }
            sendMessage.createQueueAndExchangeAndBindExchange(QUEUE_NAME + orderModel.getUserid(),
                    DIRCR_EXCHAGNE_NAME, ROUTING_KRY + orderModel.getUserid(), PAY_SUCCESS_REMIND + orderModel1.getStname());
            if (redisUtil.hasKey(String.valueOf(orderModel.getStatid()))) { //从缓存中删除此用于锁的key，方便下次使用
                redisUtil.del(String.valueOf(orderModel.getStatid()));
            }
        }else {
            Thread.interrupted();
        }
        return orderModel;
    }

    /**
     * 查询已支付订单
     *
     * @param orderModel
     * @return
     */
    @Override
    public PageInfo<OrderAndStation> findPaidOrder(OrderAndStation orderModel) {
        PageHelper.startPage(orderModel.getPagees() - 1, orderModel.getLimits());
        List<OrderAndStation> orderModelList = orderDao.findPaidOrder(orderModel);
        PageInfo<OrderAndStation> topicPageInfo = new PageInfo<>(orderModelList);
        return topicPageInfo;
    }

    /**
     * 查询未支付订单，从缓存中查找，如果缓存中没有。则表示此订单已支付,已过期或者已被取消。
     *
     * @param orderModel
     * @return
     */
    @Override
    public List<OrderAndStation> findUnpaidOrder(OrderAndStation orderModel) {
        List<OrderAndStation> list = new ArrayList<>();
        String prefix = KEY_PRE_UNPAID + orderModel.getUserid() + "*";
        Set<String> keys = redisTemplate.keys(prefix);
        // 批量获取数据
        for (String key : keys) {
            OrderAndStation orderAndStation = (OrderAndStation) redisTemplate.opsForValue().get(key);
            list.add(orderAndStation);
        }
        return ListSortUtils.sort(list);
    }

    /**
     * 取消订单，直接从订单中删除，先判断订单类型
     * 未支付：从缓存中删除。
     * 已支付：从数据库中删除。
     *
     * @param orderModel
     */
    @Override
    public void cancelOrder(OrderAndStation orderModel) {
        Integer enable = orderModel.getEnable();
        String key = KEY_PRE_UNPAID + orderModel.getUserid() + ":" + orderModel.getOrcode();
//        switch (enable) {
//            case 0:
//                synchronized (this) {
        if (redisUtil.hasKey(key)) //从缓存中检测订单是否存在，存在则删除
            redisUtil.del(key);
//                }
//                break;
//            case 1:
        String code = orderModel.getOrcode();
        orderDao.InvalidationOrder(code);
    }

    /**
     * 支付成功
     * 1.删除缓存中相应订单，并将其写入到数据库已完成的订单中去。
     * 2.将订单记录添加到对应的商家订单中去。
     *
     * @param orderAndStation1
     */
    @Override
    @Transactional //开启事务支持
    public void completePay(OrderAndStation orderAndStation1) {
        String key = KEY_PRE_UNPAID + orderAndStation1.getUserid() + ":" + orderAndStation1.getOrcode();
        OrderAndStation orderAndStation = new OrderAndStation();
        if (redisUtil.hasKey(key)) {
            //从缓存中检测订单是否存在，先取出，然后再删除
            orderAndStation = (OrderAndStation) redisTemplate.opsForValue().get(key);
            redisUtil.del(key);
        }
        orderAndStation.setCrdate(new Date());//支付成功后，设置当前最新时间。
        orderAndStation.setEnable(2);
        //将支付成功的订单，重新设置key之后放入redis,value 只保存订单号，同时其他订单数据保存在数据中。
        //将此已支付的订单写入数据库 ，设置enable =2 表示正在使用中的订单
        //之后当redis中的订单使用完毕，时间过期，redis，则发出过期通知，接受通知之后，在数据库中，将enbale改为1，已完成状态！
        redisTemplate.opsForValue().set(KEY_PRE_USING + orderAndStation.getUserid() + ":" + orderAndStation.getOrcode(), orderAndStation.getOrcode(), orderAndStation.getLetime() * 3600, TimeUnit.SECONDS);
        orderDao.usingPaid(orderAndStation.getOrcode());

        //支付成功后将消息存在数据库中
        MessageModel messageModel = new MessageModel();
        messageModel.setUserid(orderAndStation.getUserid());
        messageModel.setMetype(2);
        messageModel.setConten(PAY_SUCCESS_REMIND + orderAndStation.getStname() + "，支付完成，祝您使用愉快！");
        String key1 = KEY_PRE_MESSAGE_COUNT + orderAndStation.getUserid();
        Integer count = 0;
        if (!redisUtil.hasKey(key)) {
            redisTemplate.opsForValue().set(key, ++count, 86400 * 7, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().increment(key);
        }
        orderDao.sendMessageForOrder(messageModel);

// todo     orderDao.addOrderToBusiness(o);   如果用户支付订单成功，及时将订单数据添加到商家相关表中去，暂时未开启此功能。开启此功能，需要事务支持，以防数据没有添加成功
    }

    @Override
    public OrderAndStation findStationByOrdeid(OrderAndStation orderAndStation) {
        return orderDao.findStationByOrdeid(orderAndStation);
    }

    /**
     * 订单时间到期，将数据库中的enable改为1，已完成状态；
     *
     * @param orcode
     */
    @Override
    public void orderTimeEnd(String orcode) {
        orderDao.orderTimeEnd(orcode);
        OrderAndStation orderAndStation = orderDao.findOrderByOrcode(orcode);
        orderDao.addOrderToBusiness(orderAndStation);
    }

    /**
     * 从数据库中查询处于正在使用的订单
     *
     * @param orderAndStation
     * @return
     */
    @Override
    public List<OrderAndStation> findUsingOrder(OrderAndStation orderAndStation) {
        return orderDao.findUsingOrder(orderAndStation);
    }


    /**
     * 订单到期未支付，将订单设置为无效订单，enable=3，无效状态！
     *
     * @param orcode
     */
    @Override
    public void InvalidationOrder(String orcode) {
        orderDao.InvalidationOrder(orcode);
    }

    @Override
    public List<OrderAndStation> findInvalidationOrder(OrderAndStation orderAndStation) {
        return orderDao.findInvalidationOrder(orderAndStation.getUserid());
    }

    /**
     * 订单续租
     *
     * @param orderModel
     */
    @Override
    public void leaseRenewal(OrderAndStation orderModel) {
        //由于订单时长的变化，这里相应更新redis保存的key的过期时间，做法是先取出原剩余的过期时间，通过计算加上新添加的时间，最后将新的时间设置进去，由于key前后一样，会覆盖原来的key
        //这里没有找到直接延长redis过期时间的api,所以做法麻烦一些。
        Long expire = redisTemplate.opsForValue().getOperations().getExpire(KEY_PRE_USING + orderModel.getUserid() + ":" + orderModel.getOrcode());
        expire = expire + orderModel.getLetime() * 3600;
        redisTemplate.opsForValue().set(KEY_PRE_USING + orderModel.getUserid() + ":" + orderModel.getOrcode(), orderModel.getOrcode(), expire, TimeUnit.SECONDS);
        orderDao.leaseRenewal(orderModel);
    }

    /**
     * 查询用户个人订单列表
     *
     * @param orderModel
     * @return
     */
    @Override
    public List<OrderAndStation> findOrderByUserId(OrderAndStation orderModel) {
        List<OrderAndStation> orderModelList = orderDao.findOrderByUserId(orderModel);
        return orderModelList;
    }
}
